#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define MESSAGE_LENGTH 		1024	/*lenght of buffer*/
#define PROCFS_NAME_READ 	"read"
#define PROCFS_NAME_WRITE 	"write"

static char message[MESSAGE_LENGTH] = {0};	/*buffer*/
static int pos;		/*position in "write" buffer*/
static int wrtn_bytes;	/*written bytes*/

static DECLARE_WAIT_QUEUE_HEAD(whq);	/*queue of synchronization*/

char flag;	/*sync processes flag*/

static int procfile_open(struct inode *inode, struct  file *file)	/*the count of calls to this module (increment)*/
{
	try_module_get(THIS_MODULE);
	return 0;
}

static int procfile_close(struct inode *inode, struct file *file)	/*the count of calls to this module (decrement)*/
{
	module_put(THIS_MODULE);
	return 0;
}

static ssize_t procfile_read(struct file *filp, char *buff, size_t length, loff_t * offset)
{
	int i;		/*position in kernel buffer*/
	int j = 0;	/*position in user buffer*/
	int res = 1;	/*return 0 if time (60 seconds) is over*/
	
	if (flag == 0)		/*synchronization check*/
	  res = wait_event_interruptible_timeout(whq, flag != 0, 6000);
	
	if (res <= 0)
	  return res;
	for (i = pos; length && (i < wrtn_bytes); i++) 
	{
	  put_user(message[i], buff+j);			/*copy data from kernel space to user space (buffer)*/
	  j++;
	  length--;
	}
	
	if (i < wrtn_bytes)
	  pos = i;
	  else 
	  {
	    pos = 0;		
	    memset(message, 0, 1024);
	    flag = 0;
	    wake_up(&whq);	/*wake up write process*/
	  }
	  //printk(KERN_INFO "read i = %d \n", i);
	return i;		/* returns number of the read bytes */
}

static ssize_t procfile_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
	int i;
	int res = 1;		/*returns 1 if synchronization was successful*/
	
	if (flag != 0)		/*synchronization check*/
	  res = wait_event_interruptible(whq, flag == 0);
	
	if (res < 0)
	  return res;
	for (i = 0; i < MESSAGE_LENGTH - 1 && i < len; i++)
	  get_user(message[i], buff + i);		/*copy data from user space to kernel space (buffer)*/
	flag = 1;
	wrtn_bytes = i;
	wake_up(&whq);		/*wake up read process*/
	//printk(KERN_INFO "write i = %d \n", i);
	return i;		/* returns number of the written-down bytes */
}

static struct file_operations fops_read={
	.open 		= procfile_open,
	.read 		= procfile_read,
	.release 	= procfile_close,
	.owner 		= THIS_MODULE
};

static struct file_operations fops_write={
	.open 		= procfile_open,
	.write 		= procfile_write,
	.release 	= procfile_close,
	.owner 		= THIS_MODULE
};

int init_module()
{	
	wrtn_bytes = 0;
	flag = 0;
	pos = 0;
	
	proc_create(PROCFS_NAME_READ, 0444, NULL, &fops_read);		/*create proc/read*/
	printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME_READ);
	
	proc_create(PROCFS_NAME_WRITE, 0222, NULL, &fops_write);	/*create proc/write*/
	printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME_WRITE);
	return 0;
}

void cleanup_module()
{
  remove_proc_entry(PROCFS_NAME_READ, 0);				/*delete proc/read*/
	printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME_READ);
	
	remove_proc_entry(PROCFS_NAME_WRITE, 0);			/*delete proc/write*/
	printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME_WRITE);
}